package edu.luc.concordia;


import java.io.File;
import java.io.FileReader;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * Copyright 2008 Project Concordia
 * <p>
 * This file is part of RetailReturnSystem.
 * <p>
 * RetailReturnSystem is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <p>
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
 * <hr>
 * Read in the XML initialization file and populates the Inventory with Products and PriceSchemes as well as populates category information, member information, price schemes and tax rate.
 * @author Xingchen Su
 * @version 2.0 2008-11-19
 * Version Modifier      Date        Description
 * 0.1     Xingchen Su   2008-10-06  Initial version
 * 1.0     Xingchen Su   2008-10-09  Final version for cycle 1
 * 2.0     Xingchen Su   2008-11-19  Modified XML parser for cycle 2
 */
public class SystemInitializer {
	private ArrayList<String> mySummary; // Maintains a summary of XML parsing process
	private ArrayList<String> myErrors; // Maintains a list of errors during the XML parsing process
	/**
	 * Class constructor;Initializes internal variables to empty lists.
	 */
	public SystemInitializer() {
		mySummary = new ArrayList<String>();
		myErrors = new ArrayList<String>();
	}

	/**
	 * Populates the Inventory according to the provided XML file.
	 * @param input The xml file will be read
	 * @param inven The Inventory class object that will store the information
	 */
	public void initializeInventory(File input, Inventory inven) {
		try {
			XMLReader xr = XMLReaderFactory.createXMLReader();
			SAXCounter saxc = new SAXCounter();
			xr.setContentHandler( saxc );
			xr.parse( new InputSource(
		               new FileReader( input.getPath() )) );
			saxc.getCategoryInfo(inven);		//put category information into the inventory
			saxc.combineHashmap(inven);			//put products into the inventory
			saxc.getMemberInfo(inven);			//put member information into the inventory			
			saxc.getTaxRate(inven);				//put tax rate into the inventory
			saxc.getPriceScheme(inven);			//put price schemes into the inventory			
			mySummary=saxc.getSummary();		//get summary information
			myErrors=saxc.getErrors();			//get error information
		}catch ( Exception e )  {
	         e.printStackTrace();
	      }
	}

	/**
	 *  Returns the contents of mySummary
	 */
	public ArrayList<String> listSummary() {
		return mySummary;
	}

	/**
	 * Returns the contents of myErrors
	 */
	public ArrayList<String> listErrors() {
		return myErrors;
	}
	
	/**
	 * Creat a XML parsing handler
	 */
	private class SAXCounter extends DefaultHandler{
		private HashMap<String, ProductInfo> products; // hashmap for product information
		private HashMap<String, PriceScheme> prices; // hashmap for price scheme
		private HashMap<String, String> memberInfo; // hashmap for member information
		private HashMap<String, ArrayList<String>> categoryInfo; // hashmap for category information
		
		private String contents; // String to record the content between two tags
		
		private ProductInfo productInfo;//record temporary product information
		private PriceScheme priceScheme;//record temporary price scheme information
		private String id;//record temporary price scheme ID or category ID
		private String description;//record temporary price scheme description or category description
		private ArrayList<String> rows;//record temporary price schemes or category price ID
		private String memberID;//record temporary member ID
		private String memberName;//record temporary member name
		private String tax;//record temporary tax rate
		
		private ArrayList<String> summary;//temporary summary that will be transfered to mySummory
		private ArrayList<String> errors;//temporary errors that will be transfered to myErrors
		
		/**
		 * Class constructor.
		 */
		public SAXCounter(){
			products = new HashMap<String, ProductInfo>();
			prices = new HashMap<String, PriceScheme>();
			memberInfo = new HashMap<String, String>();
			categoryInfo = new HashMap<String, ArrayList<String>>();
			contents = "";
			id = "";
			description = "";
			rows = new ArrayList<String>();
			memberID = "";
			memberName = "";
			summary = new ArrayList<String>();
			errors = new ArrayList<String>();
		}
		
		public void startElement(String namespaceURI, String sname, String qname,Attributes atts)
		throws SAXException {
			String ename = sname; // element name
			if ("".equals(ename))
				ename = qname; // namespaceAware = false
			if (ename.equals("product")) { // when the parser encounters <product>,initialize productInfo
				productInfo = new ProductInfo();
			}
			
			if (ename.equals("systemPriceScheme")) { // when the parser encounters <systemPriceScheme>, clear rows
				rows = new ArrayList<String>();
			}
			if (ename.equals("categoryPriceScheme")) { // when the parser encounters <categoryPriceScheme>, clear rows
				rows = new ArrayList<String>();
			}
			if (ename.equals("productPriceScheme")) { // when the parser encounters <productPriceScheme>, clear rows
				rows = new ArrayList<String>();
			}
			if (ename.equals("category")) { // when the parser encounters <category>, clear rows
				rows = new ArrayList<String>();
			}
			
		}

		public void endElement(String namespaceURI, String sName,String qName)
		throws SAXException {
			String eName = sName; // element name
			if ("".equals(eName))
				eName = qName; // namespaceAware = false
			if (eName.equals("product")) { // when the parser encounters </product>,add the productInfo to hashmap
				products.put(productInfo.productId, productInfo);
			}
			if (eName.equals("productID")) { // when the parser encounters </productId>,record productId
				productInfo.productId = contents;
			}
			if (eName.equals("productDescription")) { // when the parser encounters </productDescription>, record productDescription
				productInfo.productDescription = contents;
			}
			if (eName.equals("categoryID")) { // when the parser encounters </categoryId>,record categoryId
				productInfo.categoryId = contents;
			}
			if (eName.equals("taxable")) { // when the parser encounters </taxable>,record taxable
				productInfo.taxable = contents;
			}
			if (eName.equals("schemeID")) { // when the parser encounters </schemeId>,record productPriceSchemeId
				productInfo.PriceSchemeIdList.add(contents);
			}
			
			if (eName.equals("cateID")) {
				//get category ID
				id = contents;
			}
			if (eName.equals("cateDescription")) {
				//get category description
				description = contents;
			}
			if (eName.equals("catePriceID")) {
				//get category price scheme ID
				rows.add(contents);
			}
			if (eName.equals("category")) {
				//put one category containing ID , price scheme ID and description into database
				rows.add(0,description);
				categoryInfo.put(id, rows);
			}

			if (eName.equals("memberID")) {
				//get member ID
				memberID = contents;
			}
			if (eName.equals("memberName")) {
				//get member name
				memberName = contents;
			}
			if (eName.equals("member")) {
				//put one member containing ID, name into database
				memberInfo.put(memberID, memberName);
			}

			if (eName.equals("tax")) {
				//get tax value
				tax = contents;
			}

			if (eName.equals("systemPriceSchemeID")) {
				//get system Price Scheme ID
				id = contents;
			}
			if (eName.equals("systemPriceSchemeDes")) {
				//get system Price Scheme Description
				description = contents;
			}
			if (eName.equals("systemPriceSchemeRow")) {
				//get system Price Scheme list
				rows.add(contents);
			}
			if (eName.equals("systemPriceScheme")) {
				//put one system Price Scheme containing ID, description and scheme list into database
				rows.add(0,description);
				rows.add(0,id);
				priceScheme = new PriceScheme(rows);
				prices.put(id, priceScheme);
			}
			
			if (eName.equals("categoryPriceSchemeID")) {
				//get category Price Scheme ID
				id = contents;
			}
			if (eName.equals("categoryPriceSchemeDes")) {
				//get category Price Scheme Description
				description = contents;
			}
			if (eName.equals("categoryPriceSchemeRow")) {
				//get category Price Scheme list
				rows.add(contents);
			}
			if (eName.equals("categoryPriceScheme")) {
				//put one category Price Scheme containing ID, description and scheme list into database
				rows.add(0,description);
				rows.add(0,id);
				priceScheme = new PriceScheme(rows);
				prices.put(id, priceScheme);
			}
			if (eName.equals("productPriceSchemeID")) { // when the parser encounters </productPriceSchemeId>,record PriceSchemeId
				id = contents;
			}
			if (eName.equals("productPriceSchemeDes")) { // when the parser encounters </productPriceSchemeDes>,record PriceSchemeDescription
				description = contents;
			}
			if (eName.equals("row")) { // when the parser encounters <row>,record PriceSchemeId
				rows.add(contents);				
			}
			if (eName.equals("productPriceScheme")) { // when the parser encounters </productPriceScheme>,add the PriceScheme to hashmap
				rows.add(0,description);
				rows.add(0,id);
				priceScheme = new PriceScheme(rows);
				prices.put(id, priceScheme);
			}
		}

		public void characters(char[] buf, int offset, int len)
				throws SAXException {
			contents = new String(buf, offset, len).trim();
		}
		
		/**
		 * Copy the values of temporary price schemes into inventory
		 * @param newInventory An Inventory object
		 */
		public void getPriceScheme(Inventory newInventory){
			String str = "";
			PriceScheme ps;
			Iterator< Map.Entry<String,PriceScheme> > it = prices.entrySet().iterator();
			while(it.hasNext()){
				str = "";
				Map.Entry<String,PriceScheme> mapEntry = (Map.Entry<String,PriceScheme>)it.next();
				ps = mapEntry.getValue();
				if ( newInventory.addPriceScheme(ps) ){
					str += "[Added PriceScheme: ID:"+ps.getID()+";Description:"+ps.getDescription()+";Type:"+ps.getType()+";]";
					summary.add(str);
				}
				else {
					str += "[Error PriceScheme: ID:"+ps.getID()+";]";
					errors.add(str);
				}
			}
		}
		
		/**
		 * Copy the values of temporary member information into inventory
		 * @param newInventory An Inventory object
		 */
		public void getMemberInfo(Inventory newInventory){
			String str = "";
			String memID = "";
			String memName = "";
			Iterator< Map.Entry<String,String> > it = memberInfo.entrySet().iterator();
			while(it.hasNext()){
				str = "";
				Map.Entry<String,String> mapEntry = (Map.Entry<String,String>)it.next();
				memID = mapEntry.getKey();
				memName = mapEntry.getValue();
				if ( newInventory.addMemberInfo( memID, memName) ){
					str += "[Added Member: ID:"+memID+";Name:"+memName+";]";
					summary.add(str);
				}
				else {
					str += "[Error Member: ID:"+memID+";]";
					errors.add(str);
				}
			}
		}
		
		/**
		 * Copy the values of temporary category information into inventory
		 * @param newInventory An Inventory object
		 */
		public void getCategoryInfo(Inventory newInventory){
			String str = "";
			String strID = "";
			ArrayList<String> list = new ArrayList<String>();
			Iterator< Map.Entry< String, ArrayList<String> > > it = categoryInfo.entrySet().iterator();
			while(it.hasNext()){
				str = "";
				Map.Entry< String, ArrayList<String> > mapEntry = (Map.Entry< String,ArrayList<String> >)it.next();
				strID = mapEntry.getKey();
				list = mapEntry.getValue();
				if ( newInventory.addCategory( strID, list) ){
					str += "[Added Category: ID:"+strID+";Description:"+list.get(0)+";]";
					summary.add(str);
				}
				else {
					str += "[Error Category: ID:"+strID+";]";
					errors.add(str);
				}
			}
		}
		
		/**
		 * Copy the values of temporary tax rate into inventory
		 * @param newInventory An Inventory object
		 */
		public void getTaxRate(Inventory newInventory){
			newInventory.setTaxRate( Integer.parseInt(tax) );
			String str = "[Added Tax:"+ (Integer.parseInt(tax))/100+"%;]";
			summary.add(str);
		}
		
		/**
		 * Combine products and prices into one hashmap that will be transfered to inventory later.
		 * @param newInventory An Inventory object
		 */
		public void combineHashmap(Inventory newInventory){
			Product prod;
			PriceScheme priSch;
			ArrayList<String> list;
			String strmyUID = "";
			String strmyDescription = "";
			//code to combine products and prices
			Iterator<Map.Entry<String,ProductInfo>> it = products.entrySet().iterator(); 
			while (it.hasNext()){
				prod = new Product("","");
				String str = "[Added Product:ID:";
				Map.Entry<String,ProductInfo> mapEntry = (Map.Entry<String,ProductInfo>)it.next();  
		        strmyUID = mapEntry.getKey();
		        str += strmyUID;
		        strmyDescription = mapEntry.getValue().productDescription;
		        if (strmyDescription.equals("")){
					String strE = "[Error Product:ID:" + strmyUID + ";no description;]";
					errors.add(strE);
					continue;
		        }
		        prod = new Product(strmyUID,strmyDescription);
		        str += ";DES:" + strmyDescription + ";PriceScheme:";
		        
		        list = mapEntry.getValue().PriceSchemeIdList;
		        int listsize = list.size();
		        if (listsize == 0){
		        	String strE = "[Error Product:ID:" + strmyUID + ";no ProductPriceScheme;]";
					errors.add(strE);
					continue;
		        }
		        //add product price schemes into product
		        for (int i=0;i<listsize;i++){
		        	priSch = prices.get(list.get(i));
		        	str += priSch.getID();
		        	str += "," + priSch.getDescription() + "," + priSch.getType() + ";";
		        	prod.addPriceScheme(priSch);
		        }
		        //add category price schemes into product
		        list = categoryInfo.get(mapEntry.getValue().categoryId);
		        listsize = list.size();
		        for (int i=1;i<listsize;i++){
		        	priSch = prices.get(list.get(i));
		        	str += priSch.getID();
		        	str += "," + priSch.getDescription() + "," + priSch.getType() + ";";
		        	prod.addPriceScheme(priSch);
		        }
		        //add system price schemes into product
		        Iterator< Map.Entry<String,PriceScheme> > itPs = prices.entrySet().iterator();
				while(itPs.hasNext()){
					Map.Entry<String,PriceScheme> mapEntryPs = (Map.Entry<String,PriceScheme>)itPs.next();
					if (mapEntryPs.getValue().getType()=="system"){
						priSch = mapEntryPs.getValue();
			        	str += priSch.getID();
			        	str += "," + priSch.getDescription() + "," + priSch.getType() + ";";
			        	prod.addPriceScheme(priSch);
					}
				}
		        if (mapEntry.getValue().taxable == "0"){
		        	prod.setIsTaxable(false);
		        	str += "untaxable;";
		        }
		        else {
		        	prod.setIsTaxable(true);
		        	str += "taxable;";
		        }
		        prod.setCategoryID( mapEntry.getValue().categoryId );
		        str += "categoryID:"+mapEntry.getValue().categoryId+";]";
		        summary.add(str);
		        newInventory.addProduct(prod);
			}
		}
		
		/**
		 * Get the summary
		 */
		public ArrayList<String> getSummary(){
			return summary;
		}
		
		/**
		 * Get the errors
		 */
		public ArrayList<String> getErrors(){
			return errors;
		}

	}
}


